package user_api

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis"
	"giv-vue-blog/global"
	"giv-vue-blog/models"
	"giv-vue-blog/models/res"
	"giv-vue-blog/plugins/email"
	"giv-vue-blog/utils/jwts"
	"giv-vue-blog/utils/pwd"
	"giv-vue-blog/utils/random"
	"regexp"
	"time"
)

type GetCodeRequest struct {
	Email    string  `json:"email" binding:"required,email" msg:"邮箱非法"`
	PassWord string  `json:"password" msg:"请输入密码"`
	Code     *string `json:"code"`
}

// UserBindEmail   用户绑定邮箱
// @Summary 用户绑定邮箱
// @Description 用户绑定邮箱
// @Tags 用户管理
// @Accept json
// @Produce json
// @Param request body GetCodeRequest true "绑定邮箱请求体"
// @Security BearerAuth
// @Success 200 {string} string "邮箱绑定成功"
// @Router /api/user_bind_email [post]
func (UserApi) UserBindEmail(c *gin.Context) {
	/*
		获取登录用户的信息
	*/
	_claims, _ := c.Get("claims")
	claims := _claims.(*jwts.CustomClaims)
	/*
		这段代码的目的是在数据库中查找特定用户，具体查询条件是用户的 id 字段与给定的 claims.UserID 值匹配。
		如果查询成功，将提取出的邮箱地址赋值给 user 变量。
		fmt.Println(claims.UserID, user.Email)
	*/
	var user models.UserModel
	global.DB.Model(&user).Where("id=?", claims.UserID).Select("email").First(&user)
	/*
		根据claims.UserID来判断这个用户的 user.Email 是否等于空字符串 ""，将布尔值赋给 isEmpty 变量,
		如果为空，则isEmpty为true
		var isEmpty bool
		if user.Email == "" {
		    isEmpty = true
		} else {
		    isEmpty = false
		}
	*/
	isEmpty := user.Email == ""
	if isEmpty {
		//用户绑定邮箱  第一次输入是 邮箱
		//TODO: 绑定结构体 后台会给这个邮箱发送验证码
		var cr GetCodeRequest
		if err := c.ShouldBindJSON(&cr); err != nil {
			res.FailedWithError(err, &cr, c)
			return
		}
		//TODO: 如果第一次请求时code是空的，则发送一个4位的邮箱验证码 random.Code(4)可以指定验证码的长度
		if cr.Code == nil {
			// 第一次，发送验证码
			code := random.Code(4)
			//TODO: 这里调用redis，写进去一个valid_email:cr.Email的key 有效期是10分钟 记录的是发验证码的邮箱
			if err := global.Redis.Set("valid_email:"+cr.Email, cr.Email, 10*time.Minute).Err(); err != nil {
				global.Log.Error(err)
				res.FailedWithMsg("操作失败，请稍后再试", c)
				return
			}
			//TODO: 这里调用redis，写进去一个valid_code:cr.Email的key 有效期是10分钟 记录的是验证码code
			if err := global.Redis.Set("valid_code:"+cr.Email, code, 10*time.Minute).Err(); err != nil {
				global.Log.Error(err)
				res.FailedWithMsg("操作失败，请稍后再试", c)
				return
			}
			//TODO: 这里调用发送验证码的邮箱接口 发送验证码
			if err := email.NewCode().Send(cr.Email, "你的验证码是: "+code); err != nil {
				global.Log.Error(err)
			}
			res.OkWithMsg("验证码已发送,请查收", c)
			return
		}
		// 第二次，进行绑定邮箱操作
		// TODO: 这里调用redis, 拿到key为valid_email:cr.Email的值value 即这个指的就是redis中存储的邮箱地址的
		validEmail, err := global.Redis.Get("valid_email:" + cr.Email).Result()
		//fmt.Println("validEmail:", validEmail)
		if err != nil {
			if err == redis.Nil {
				res.FailedWithMsg("邮箱验证码已过期，请重新发送", c)
				return
			}
			global.Log.Error(err)
			res.FailedWithMsg("操作失败，请稍后再试", c)
			return
		}
		// TODO: 这里调用redis, 拿到key为valid_code:cr.Email的值value 即这个指的就是redis中存储的验证码
		validCode, err := global.Redis.Get("valid_code:" + cr.Email).Result()
		if err != nil {
			if err == redis.Nil {
				res.FailedWithMsg("邮箱验证码已过期，请重新发送", c)
				return
			}
			global.Log.Error(err)
			res.FailedWithMsg("操作失败，请稍后再试", c)
			return
		}
		// TODO: 判断redis中存储的code值是否等于绑定的code值
		if validCode != *cr.Code {
			res.FailedWithMsg("验证码错误", c)
			return
		}
		// TODO: 这里调用claims.UserID, 这个claims.UserID指的就是产生token值对应的用户ID 即指明是哪一个用户正在做绑定邮箱这件事，
		// TODO: 通过claims.UserID来查找该用户是否存在
		//var user models.UserModel
		err = global.DB.Take(&user, claims.UserID).Error
		if err != nil {
			res.FailedWithMsg("用户不存在", c)
			return
		}
		// TODO: 这里可以使用密码校验器  采用校验器绑定的方法 包含大小写字母、数字、特殊字符 必须是6-12位
		if ok := regexp.MustCompile(`^[0-9a-zA-Z!@#$%^&*()_+|<>,.?/~\[\]{}\-=:;'"]{6,12}$`).MatchString(cr.PassWord); !ok {
			res.FailedWithMsg("新密码不符合要求", c)
			return
		}
		// TODO: 调用密码加密接口 对输入的密码进行加密
		hashPwd := pwd.HashAndSalt(cr.PassWord)
		// TODO: 校验发送验证码的邮箱是否等于此刻绑定的邮箱，解决发送验证码邮箱与绑定邮箱不一致的情况
		if validEmail != cr.Email {
			fmt.Println("code:", *cr.Code)
			res.FailedWithMsg("邮箱不一致", c)
			return
		}
		// TODO: 绑定邮箱与密码
		if err := global.DB.Model(&user).Updates(map[string]interface{}{
			"email":    validEmail,
			"password": hashPwd,
		}).Error; err != nil {
			global.Log.Error(err)
			res.FailedWithMsg("绑定邮箱失败", c)
			return
		}
		// TODO: 完成绑定
		res.OkWithMsg("邮箱绑定成功", c)
		return
	} else {
		global.Log.Info(fmt.Sprintf("用户ID为%d的账户已绑定了邮箱", claims.UserID))
		res.FailedWithMsg(fmt.Sprintf("用户ID为%d的账户已绑定了邮箱", claims.UserID), c)
		return
	}
}
